#include "bd.h"
#include <stdio.h>
#include <stdlib.h>
#include <libpq-fe.h>

BDConnection *bdConnect(BDPair *params)
{
  BDConnection *conn=0;
  PGconn *pgconn=0;
  char buf[4096];
  int bsz = 0;
  int i;

  for(i=0; params[i].name; i++)
  {
    bsz += snprintf(buf+bsz,4096-bsz,"%s=%s ",params[i].name,params[i].value);
    if(bsz>=4096) goto ERR;
  }

  conn = (BDConnection *)malloc(sizeof(BDConnection));
  if(!conn) goto ERR;

  pgconn = PQconnectdb(buf);
  if(PQstatus(pgconn)!=CONNECTION_OK)
  {
    fprintf(stderr,"CONNECT error: %s\n",PQerrorMessage(pgconn));
    goto ERR;
  }

  conn->data = pgconn;
  return conn;

ERR:
  if(conn) free(conn);
  if(pgconn) PQfinish(pgconn);
  return 0;
}

void bdDisconnect(BDConnection *connection)
{
  PGconn *pgconn = (PGconn *)connection->data;
  if(pgconn) PQfinish(pgconn);
  free(connection);
}

BDConnectionStatus bdConnectionStatus(BDConnection *conn)
{
  PGconn *pgconn=(PGconn *)conn->data;
  if(PQstatus(pgconn)==CONNECTION_OK) return BD_C_OK;

  return BD_C_NOT_OK;
}

BDError bdTransactionCmd(PGconn *pgconn, char *cmd)
{
  BDError result = BD_E_ERROR;

  PGresult *res = PQexec(pgconn, cmd);

  if(PQresultStatus(res) == PGRES_COMMAND_OK) result = BD_E_NONE;
  else fprintf(stderr,"Transaction %s error: %s\n",cmd,PQerrorMessage(pgconn));

  PQclear(res);

  return result;
}

BDError bdBeginTransaction(BDConnection *conn)
{
  PGconn *pgconn=(PGconn *)conn->data;

  return bdTransactionCmd(pgconn,"BEGIN");
}

BDError bdCommitTransaction(BDConnection *conn)
{
  PGconn *pgconn=(PGconn *)conn->data;

  return bdTransactionCmd(pgconn,"COMMIT");
}

BDError bdRollbackTransaction(BDConnection *conn)
{
  PGconn *pgconn=(PGconn *)conn->data;

  return bdTransactionCmd(pgconn,"ROLLBACK");
}

BDTransactionStatus bdTransactionStatus(BDConnection *conn)
{
  PGconn *pgconn=(PGconn *)conn->data;
  BDTransactionStatus result = BD_T_NOT_DEFINED;

  switch(PQtransactionStatus(pgconn))
  {
    case PQTRANS_IDLE: result = BD_T_IDLE; break;
    case PQTRANS_ACTIVE: result = BD_T_OK; break;
    case PQTRANS_INTRANS: result = BD_T_OK; break;
    case PQTRANS_INERROR: result = BD_T_ERR; break;
    case PQTRANS_UNKNOWN: result = BD_T_ERR; break;
  }

  return result;
}

//insert,delete,update
BDError bdExecCommand(BDConnection *conn, char *query, char *values[], int numParams)
{
  PGconn *pgconn=(PGconn *)conn->data;
  int result = BD_E_ERROR;

  PGresult *res = PQexecParams(pgconn, query, numParams, 0, (const char **)values, 0, 0, 0);

  if(PQresultStatus(res) == PGRES_COMMAND_OK) result = BD_E_NONE;
  else fprintf(stderr,"COMMAND error: %s\n",PQerrorMessage(pgconn));

  PQclear(res);

  return result;
}

//select
BDResult *bdExecQuery(BDConnection *conn, char *query, char *values[], int numParams)
{
  PGconn *pgconn=(PGconn *)conn->data;
  BDResult *bdResult = 0;

  PGresult *res = PQexecParams(pgconn, query, numParams, 0, (const char **)values, 0, 0, 0);
  if(PQresultStatus(res)==PGRES_TUPLES_OK)
  {
    bdResult = (BDResult *)malloc(sizeof(BDResult));
    if(!bdResult) goto ERR;

    bdResult->rows = PQntuples(res);
    bdResult->cols = PQnfields(res);
    bdResult->currRow = -1;
    bdResult->data = res;
  }
  else
  {
    fprintf(stderr,"QUERY error: %s\n",PQerrorMessage(pgconn));
    goto ERR;
  }

  return bdResult;

ERR:
  PQclear(res);
  if(bdResult) free(bdResult);
  return 0;
}

char *bdResultColName(BDResult *result, long num)
{
  PGresult *res = (PGresult *)result->data;
  return PQfname(res,num);
}

int bdNextRow(BDResult *result)
{
  result->currRow++;
  if(result->currRow>=result->rows) return 0;

  return 1;
}

char *bdResultVal(BDResult *result, long col)
{
  PGresult *res = (PGresult *)result->data;

  if(PQgetisnull(res,result->currRow,col)) return 0;
  return PQgetvalue(res,result->currRow,col);
}

void bdFreeResult(BDResult *result)
{
  PQclear(result->data);
  free(result);
}

char *bdType()
{     
  static char *type = "postgres";
  
  return type;
}

